﻿/// Name - Common.Cryptography v1.0
/// Description - This class controls cryptographic functions.
///     Initially, this was designed to create an encryption string for use with verifying an email address 
///     exists.  The result of the encryption algorhythm would be emailed to the user, then the user would
///     enter the string in the web interface to validate the email.
/// Programmer - Dan Yatzeck
/// Date coded - 10/26/2011
/// Arguments - None
/// Variables - None
/// Files accessed by this code artifact - None
/// Files changed by this code artifact - None
/// I/O - None
/// Error Handling - None
/// Modifications -
///     1.0 - Initial coding, copied from http://www.dijksterhuis.org/encrypting-decrypting-string/
/// Known Faults -
///     1.0 - DecryptString could throw an exception if the input string could not be converted into a UTF8 
///         byte array.  VerifyCypherText is used to code around this.

using System;
using System.Security.Cryptography;

namespace Common
{
    /// <summary>
    /// Cryptographic algorhythms and other functions related to encryption/decryption.
    /// </summary>
    public class Cryptography
    {
        /// <summary>
        /// Creates a base64 encoded cyphertext based on a message and passphrase.
        /// </summary>
        /// <param name="Message">Data to encrypt</param>
        /// <param name="Passphrase">Secret key for decryption</param>
        /// <returns>Encrypted cyphertext of the input plaintext</returns>
        public static string EncryptString(string Message, string Passphrase)
        {
            byte[] Results;
            System.Text.UTF8Encoding UTF8 = new System.Text.UTF8Encoding();

            // Step 1. We hash the passphrase using MD5
            // We use the MD5 hash generator as the result is a 128 bit byte array
            // which is a valid length for the TripleDES encoder we use below

            MD5CryptoServiceProvider HashProvider = new MD5CryptoServiceProvider();
            byte[] TDESKey = HashProvider.ComputeHash(UTF8.GetBytes(Passphrase));

            // Step 2. Create a new TripleDESCryptoServiceProvider object
            TripleDESCryptoServiceProvider TDESAlgorithm = new TripleDESCryptoServiceProvider();

            // Step 3. Setup the encoder
            TDESAlgorithm.Key = TDESKey;
            TDESAlgorithm.Mode = CipherMode.ECB;
            TDESAlgorithm.Padding = PaddingMode.PKCS7;

            // Step 4. Convert the input string to a byte[]
            byte[] DataToEncrypt = UTF8.GetBytes(Message);

            // Step 5. Attempt to encrypt the string
            try
            {
                ICryptoTransform Encryptor = TDESAlgorithm.CreateEncryptor();
                Results = Encryptor.TransformFinalBlock(DataToEncrypt, 0, DataToEncrypt.Length);
            }
            finally
            {
                // Clear the TripleDes and Hashprovider services of any sensitive information
                TDESAlgorithm.Clear();
                HashProvider.Clear();
            }

            // Step 6. Return the encrypted string as a base64 encoded string
            return Convert.ToBase64String(Results);
        }

        /// <summary>
        /// Decrypts a message that was previously encoded with the EncryptString method.
        /// </summary>
        /// <param name="Message">Cyphertext to be decrypted</param>
        /// <param name="Passphrase">Secret key for decryption</param>
        /// <returns>Decrypted plaintext version of the encrypted cyphertext</returns>
        public static string DecryptString(string Message, string Passphrase)
        {
            byte[] Results;
            System.Text.UTF8Encoding UTF8 = new System.Text.UTF8Encoding();

            // Step 1. We hash the passphrase using MD5
            // We use the MD5 hash generator as the result is a 128 bit byte array
            // which is a valid length for the TripleDES encoder we use below

            MD5CryptoServiceProvider HashProvider = new MD5CryptoServiceProvider();
            byte[] TDESKey = HashProvider.ComputeHash(UTF8.GetBytes(Passphrase));

            // Step 2. Create a new TripleDESCryptoServiceProvider object
            TripleDESCryptoServiceProvider TDESAlgorithm = new TripleDESCryptoServiceProvider();

            // Step 3. Setup the decoder
            TDESAlgorithm.Key = TDESKey;
            TDESAlgorithm.Mode = CipherMode.ECB;
            TDESAlgorithm.Padding = PaddingMode.PKCS7;

            // Step 4. Convert the input string to a byte[]
            byte[] DataToDecrypt = Convert.FromBase64String(Message);

            // Step 5. Attempt to decrypt the string
            try
            {
                ICryptoTransform Decryptor = TDESAlgorithm.CreateDecryptor();
                Results = Decryptor.TransformFinalBlock(DataToDecrypt, 0, DataToDecrypt.Length);
            }
            catch
            {
                // Clear the TripleDes and Hashprovider services of any sensitive information
                TDESAlgorithm.Clear();
                HashProvider.Clear();

                return "failed";
            }
            finally
            {
                // Clear the TripleDes and Hashprovider services of any sensitive information
                TDESAlgorithm.Clear();
                HashProvider.Clear();
            }

            // Step 6. Return the decrypted string in UTF8 format
            return UTF8.GetString(Results);
        }

        /// <summary>
        /// Returns true if the cyphertext is a base64 encoded string, which would be true if it was the
        /// result of the EncryptString method.
        /// </summary>
        /// <param name="cypherText">Cyphertext to be verified</param>
        /// <returns>True if the cyphertext is base64 encoded, false otherwise.</returns>
        public static bool VerifyCypherText(string cypherText)
        {
            //if (cypherText.Length != 32) { return false; }
            try
            {
                byte[] data = Convert.FromBase64String(cypherText);
            }
            catch
            {
                return false;
            }

            return true;
        }
    }
}
